package cn.shop.admin.component.withdraw.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.shop.admin.component.withdraw.WithdrawStrategy;
import cn.shop.admin.config.FkPayConfig;
import cn.shop.admin.config.PayConfig;
import cn.shop.admin.req.BatchWithdrawReq;
import cn.shop.admin.service.WithdrawService;
import cn.shop.transcation.backPaymentChannelModule.entity.BackPaymentChannel;
import cn.shop.transcation.backPaymentChannelModule.mapper.BackPaymentChannelMapper;
import cn.shop.transcation.withdraw.entity.Withdraw;
import cn.shop.utils.PayUtil;
import cn.shop.utils.enums.ProcessState;
import cn.shop.utils.enums.WithdrawAccountType;
import cn.shop.utils.enums.WithdrawSolution;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 代付结算
 *
 * @author jzw
 * @since 2021-11-11 10:08
 */
@Slf4j
@Component
public class PaymentOnBehalfStrategy implements WithdrawStrategy {

	@Resource
	private WithdrawService withdrawService;

	@Resource
	private BackPaymentChannelMapper paymentChannelMapper;

	@Resource
	private PayConfig payConfig;
	@Resource
	private FkPayConfig fkPayConfig;

	@Resource
	private AsyncTaskExecutor asyncTaskExecutor;
	@Resource
	private RedissonClient redissonClient;

	/**
	 * 获取当前提现类型标识
	 *
	 * @return 提现类型标识
	 * @author jzw
	 * @since 2021/11/11 10:36
	 */
	@Override
	public int getType() {
		return 1;
	}

	/**
	 * 订单结算
	 *
	 * @param batchWithdrawReq 订单id
	 * @return
	 * @author jzw
	 * @since 2021/11/11 10:09
	 */
	@Override
	public boolean settlement(BatchWithdrawReq batchWithdrawReq) {
		asyncTaskExecutor.submit(() -> {
			log.info("异步支付任务开始");
			executePay(batchWithdrawReq);
			log.info("异步支付任务结束");
		});
		return true;
	}

	private void executePay(BatchWithdrawReq batchWithdrawReq) {
		List<Withdraw> withdraws = withdrawService.lambdaQuery()
				.in(Withdraw::getId, batchWithdrawReq.getIds())
				.eq(Withdraw::getAccountType, WithdrawAccountType.BANK_PAYMENT)
				.eq(Withdraw::getState, ProcessState.UNTREATED)
				.list();
		if (CollectionUtil.isEmpty(withdraws)) {
			return;
		}
		BackPaymentChannel backPaymentChannel = paymentChannelMapper.selectById(batchWithdrawReq.getId());
		for (Withdraw withdraw : withdraws) {
			RLock lock = redissonClient.getLock("withdraw:" + withdraw.getId());
			try {
				if (lock.tryLock(0, 5, TimeUnit.MINUTES)) {
					sendSettlementRequest(withdraw, backPaymentChannel, batchWithdrawReq.getPaymentPassword(), batchWithdrawReq.getScheme());
					withdrawService.updateBatchById(withdraws);
				}
			} catch (Exception e) {
				log.error("提现打款出错，订单信息：{}", withdraw, e);
			} finally {
				if (lock.isLocked() && lock.isHeldByCurrentThread()) {
					lock.unlock();
				}
			}
		}
	}

	private void sendSettlementRequest(Withdraw withdraw, BackPaymentChannel backPaymentChannel, String paymentPassword, String scheme) {
		log.info("银行卡代付====== 1 , 通道id:{}, 提现订单id: {} , 开始提现", backPaymentChannel, withdraw.getId());
		Dict dict = Dict.create();
		//价格*100 转换成分
		String notifyUrl = fkPayConfig.getNotify()
				.replace("{origin}", scheme);
		dict.set("amount", NumberUtil.mul(Convert.toBigDecimal(withdraw.getActualAmount()), 100)).
				set("banktype", withdraw.getBankName()).
				set("bankname", withdraw.getBankAccountName()).
				set("banknumber", withdraw.getAccount()).
				set("mobile", withdraw.getPhone()).
				set("idcard", withdraw.getIdCard()).
				set("pay_key", backPaymentChannel.getPassword()).
				set("merchant_id", backPaymentChannel.getMerchantNumber());
		withdraw.setPaymentChannelId(backPaymentChannel.getId());
		String code = PayUtil.getSignToken(dict, backPaymentChannel.getSecret());
		dict.set("code", code);
		String url;
		// 创建支付 订单
		switch (backPaymentChannel.getApiType()) {
			case DY_PAYMENT:
				url = payConfig.getUrl();
				processWithdraw(url, dict, backPaymentChannel, withdraw);
				break;
			case HX_PAYMENT:
				url = payConfig.getHxUrl();
				processWithdraw(url, dict, backPaymentChannel, withdraw);
				break;
			case FK_PAYMENT:
				url = fkPayConfig.getUrl();
				if (backPaymentChannel.getMerchantNumber().equals(fkPayConfig.getNewUser())) {
					url = fkPayConfig.getNewUrl();
				}
				processWithdraw(url, backPaymentChannel, withdraw, paymentPassword, notifyUrl);
				break;
			default:
				log.warn("代付渠道错误。{}", backPaymentChannel);
				break;
		}

	}

	private void processWithdraw(String url, Dict dict, BackPaymentChannel channel, Withdraw withdraw) {
		String post = HttpUtil.post(url, dict);
		log.info("代付请求地址：{}，银行卡代付====== 2 , 通道id:{}, 提现订单id: {} , 发送请求成功,响应内容:{}", url, channel.getId(), withdraw.getId(), post);

		JSONObject map = JSONUtil.parseObj(post);
		int apiCode = (int) map.get("api_code");

		withdraw.setSolution(WithdrawSolution.AUTOMATIC_PAYMENT);
		if (apiCode == 200) {
			withdraw.setState(ProcessState.PROCESSING);
		}
		else {
			withdraw.setState(ProcessState.ABNORMAL);
			withdraw.setExceptionInfo((String) map.get("api_msg"));
		}
	}

	private void processWithdraw(String url, BackPaymentChannel channel, Withdraw withdraw, String paymentPassword, String notifyUrl) {
		Dict dict = Dict.create();
		//价格*100 转换成分
		dict.set("amount", NumberUtil.mul(Convert.toBigDecimal(withdraw.getActualAmount()), 100).intValue())
				.set("bankType", withdraw.getBankName())
				.set("requestId", withdraw.getId())
				.set("bankName", withdraw.getBankAccountName())
				.set("bankNumber", withdraw.getAccount())
				.set("payKey", channel.getPassword())
				.set("notifyUrl", notifyUrl)
				.set("merchantId", channel.getMerchantNumber());
		String code = PayUtil.getSignToken(dict, channel.getSecret());
		dict.set("code", code);
		String json = JSONUtil.toJsonStr(dict);
		String post = HttpUtil.post(url, json);
		log.info("银行卡代付====== 2 , 通道id:{}, 提现订单id: {} , 发送请求成功,响应内容:{}", channel.getId(), withdraw.getId(), post);
		JSONObject map = JSONUtil.parseObj(post);
		int apiCode = (int) map.get("code");
		withdraw.setSolution(WithdrawSolution.AUTOMATIC_PAYMENT);
		if (apiCode == 10000) {
			withdraw.setState(ProcessState.PROCESSING);
		}
		else {
			withdraw.setState(ProcessState.ABNORMAL);
			withdraw.setExceptionInfo((String) map.get("msg"));
		}
	}

}
